package com.lagou.rpc.provider.server;

import com.alibaba.fastjson.JSON;
import com.lagou.rpc.common.RpcRequest;
import com.lagou.rpc.common.RpcResponse;
import com.lagou.rpc.provider.anno.RPCService;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.SneakyThrows;
import org.springframework.cglib.reflect.FastClass;
import org.springframework.cglib.reflect.FastMethod;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@Component
@ChannelHandler.Sharable
public class RpcServerHandler extends SimpleChannelInboundHandler<String> implements ApplicationContextAware {

    private  static Map SERVER_INSTANS_MAP = new ConcurrentHashMap();
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
        RpcRequest rpcRequest = JSON.parseObject(msg, RpcRequest.class);
        RpcResponse rpcResponse = new RpcResponse();
        try {
            rpcResponse.setResult(handler(rpcRequest));
        } catch (Exception exception) {
            rpcResponse.setError(exception.getMessage());
        }
        channelHandlerContext.writeAndFlush(JSON.toJSONString(rpcResponse) );

    }

    private Object handler(RpcRequest rpcRequest) throws InvocationTargetException {
        String className = rpcRequest.getClassName();
        Object service = SERVER_INSTANS_MAP.get(className);
        Class<?> aClass = service.getClass();
        Object[] parameters = rpcRequest.getParameters();
        String methodName = rpcRequest.getMethodName();
        FastClass fastClass = FastClass.create(aClass);
        Class<?>[] parameterTypes = rpcRequest.getParameterTypes();
        FastMethod method = fastClass.getMethod(methodName, parameterTypes);

        return  method.invoke(service,parameters);


    }

    @SneakyThrows
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(RPCService.class);
        if(!beansWithAnnotation.isEmpty()){
            Set<Map.Entry<String, Object>> entries = beansWithAnnotation.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                Object obj = entry.getValue();
                if(obj.getClass().getInterfaces().length==0){
                    throw new Exception("服务必须实现接口");
                }else {
                    String name = obj.getClass().getInterfaces()[0].getName();
                    SERVER_INSTANS_MAP.put(name,obj);
                }
            }
        }

    }
}
